#include "LumineContext.hpp"
#include <map>
#include <iostream>
#include <set>

namespace Lumine
{
    //===============================================================
    //                      Vulkan组件创建函数
    //===============================================================
    void LumineContext::createInstance()
    {
        vk::ApplicationInfo appInfo("LumineVulkan", 1, "No Engine", 1, VK_API_VERSION_1_3);
        vk::InstanceCreateInfo createInfo;
        std::vector<const char *> instExts = getRequiredInstanceExtensions();
        createInfo.setPApplicationInfo(&appInfo)
            .setEnabledExtensionCount(static_cast<uint32_t>(instExts.size()))
            .setPpEnabledExtensionNames(instExts.data());
        createInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());
        createInfo.ppEnabledLayerNames = instanceLayers.data();
        m_instance = vk::raii::Instance(m_context, createInfo);
    }

#ifndef NDEBUG
    void LumineContext::setupDebugMessenger()
    {
        vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError);
        vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags(vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation);
        vk::DebugUtilsMessengerCreateInfoEXT info;
        info.messageSeverity = severityFlags;
        info.messageType = messageTypeFlags;
        info.pfnUserCallback = &debugCallback;
        m_debugMessenger = m_instance.createDebugUtilsMessengerEXT(info);
    }
#endif

    void LumineContext::createSurface(GLFWwindow *m_window)
    {
        VkSurfaceKHR surface_c;
        if (glfwCreateWindowSurface(*m_instance, m_window, nullptr, &surface_c) != VK_SUCCESS)
        {
            throw std::runtime_error("failed to create window surface!");
        }
        m_surface = vk::raii::SurfaceKHR(m_instance, surface_c);
    }

    void LumineContext::createLogicDevice()
    {
        findQueueFamilies();
        std::set<uint32_t> queueFamilies = {
            indices.graphicsFamily.value(),
            indices.presentFamily.value(),
            indices.transferFamily.value()};
        std::vector<vk::DeviceQueueCreateInfo> createInfos;
        float queuePriority = 1.0f;
        for (uint32_t item : queueFamilies)
        {
            vk::DeviceQueueCreateInfo info;
            info.setQueueFamilyIndex(item)
                .setQueueCount(1)
                .setPQueuePriorities(&queuePriority);
            createInfos.push_back(info);
        }

        // 加载Vulkan1.3新特性
        vk::PhysicalDeviceFeatures2 pdf2;
        vk::PhysicalDeviceFeatures pdf;
        pdf.setSamplerAnisotropy(true);
        pdf2.setFeatures(pdf);
        vk::PhysicalDeviceVulkan13Features pdv13f;
        pdv13f.dynamicRendering = true;
        pdv13f.setSynchronization2(true);
        vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT pdedsf;
        pdedsf.extendedDynamicState = true;

        vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {pdf2, pdv13f, pdedsf};

        // 设置启用
        vk::DeviceCreateInfo info;
        info.setPNext(&featureChain.get<vk::PhysicalDeviceFeatures2>())
            .setPQueueCreateInfos(createInfos.data())
            .setQueueCreateInfoCount(createInfos.size())
            .setEnabledExtensionCount(static_cast<uint32_t>(deviceExtensions.size()));
        info.ppEnabledExtensionNames = deviceExtensions.data();

        m_device = vk::raii::Device(m_physicalDevice, info);
        m_graphicQueue = m_device.getQueue(indices.graphicsFamily.value(), 0);
        m_presentQueue = m_device.getQueue(indices.presentFamily.value(), 0);
        m_transferQueue = m_device.getQueue(indices.transferFamily.value(), 0);
        // m_graphicQueue = vk::raii::Queue(m_device, indices.graphicsFamily.value(), 0);
        // m_presentQueue = vk::raii::Queue(m_device, indices.presentFamily.value(), 0);
        // m_transferQueue = vk::raii::Queue(m_device, indices.transferFamily.value(), 0);
    }

    void LumineContext::pickPhysicalDevice()
    {
        auto devices = m_instance.enumeratePhysicalDevices();
        if (devices.empty())
        {
            throw std::runtime_error("failed to find GPUs with Vulkan support!");
        }
        std::multimap<int, vk::raii::PhysicalDevice> candidates;

        for (const auto &device : devices)
        {
            auto props = device.getProperties();
            auto features = device.getFeatures();
            uint32_t score = 0;
            if (props.deviceType == vk::PhysicalDeviceType::eDiscreteGpu)
            {
                score += 1000;
            }
            score += props.limits.maxImageDimension2D;
            if (!features.geometryShader)
            {
                continue;
            }
            candidates.insert(std::make_pair(score, device));
        }
        if (candidates.rbegin()->first > 0)
        {
            m_physicalDevice = vk::raii::PhysicalDevice(candidates.rbegin()->second);
        }
        else
        {
            throw std::runtime_error("failed to find a suitable GPU!");
        }
    }

    //===============================================================
    //                          对外公开调用函数
    //===============================================================
    void LumineContext::InitializeInstance(GLFWwindow *window)
    {
        createInstance();
        setupDebugMessenger();
        createSurface(window);
    }

    void LumineContext::InitializeDevice()
    {
        pickPhysicalDevice();
        createLogicDevice();
    }

    //===============================================================
    //                         工具函数
    //===============================================================
    std::vector<const char *> LumineContext::getRequiredInstanceExtensions()
    {
        uint32_t count = 0;
        const char **glfwExts = glfwGetRequiredInstanceExtensions(&count);
        std::vector<const char *> exts(glfwExts, glfwExts + count);
#ifndef NDEBUG
        exts.push_back(vk::EXTDebugUtilsExtensionName);
#endif
        return exts;
    }

    void LumineContext::findQueueFamilies()
    {
        std::vector<vk::QueueFamilyProperties> qfp = m_physicalDevice.getQueueFamilyProperties();

        for (uint32_t i = 0; i < qfp.size(); ++i)
        {
            // 查找图像队列族
            if (qfp[i].queueFlags & vk::QueueFlagBits::eGraphics)
            {
                graphicIndex = i;
                indices.graphicsFamily = i;
            }
            // 查找呈现队列
            if (m_physicalDevice.getSurfaceSupportKHR(i, m_surface))
            {
                presentIndex = i;
                indices.presentFamily = i;
            }
            if (indices.graphicsFamily.has_value() && indices.presentFamily.has_value())
            {
                break;
            }
        }
        uint32_t i = 0;
        for (const auto &item : qfp)
        {
            auto canTransfer = item.queueFlags & vk::QueueFlagBits::eTransfer;
            auto canGraphics = item.queueFlags & vk::QueueFlagBits::eGraphics;
            auto canPresent = item.queueFlags & vk::QueueFlagBits::eCompute;
            if (!indices.transferFamily.has_value() && canTransfer && !canGraphics && !canPresent)
            {
                transferIndex = i;
                indices.transferFamily = i;
                break;
            }
            i++;
        }
        if (!indices.transferFamily.has_value() && indices.graphicsFamily.has_value())
        {
            indices.transferFamily = indices.graphicsFamily;
            transferIndex = graphicIndex;
        }
    }

    vk::Result LumineContext::checkInstanceLayers(std::span<const char *> layerToCheck)
    {
        auto avalibleLayers = m_context.enumerateInstanceLayerProperties();
        for (auto &i : layerToCheck)
        {
            bool found = false;
            for (auto &j : avalibleLayers)
            {
                if (!strcmp(i, j.layerName))
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                i = nullptr;
            }
        }
        return vk::Result::eSuccess;
    }

    vk::Result LumineContext::checkInstanceExtensions(std::span<const char *> extensionToCheck, const char *layerName)
    {
        auto availableExtensions = m_context.enumerateInstanceExtensionProperties();
        for (auto &i : extensionToCheck)
        {
            bool found = false;
            for (auto &j : availableExtensions)
            {
                if (!strcmp(i, j.extensionName))
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                i = nullptr;
            }
        }
        return vk::Result::eSuccess;
    }

    /// @brief
    /// @param severity
    /// @param type
    /// @param pCallbackData
    /// @param
    /// @return
    VKAPI_ATTR vk::Bool32 VKAPI_CALL LumineContext::debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, const vk::DebugUtilsMessengerCallbackDataEXT *pCallbackData, void *)
    {
        std::cerr << "validation layer: type " << to_string(type) << " msg: " << pCallbackData->pMessage << std::endl;

        return vk::False;
    }
}